package controller

import (
	"encoding/json"
	"fmt"
	"strconv"
	"sugar/database"
	"sugar/errorcode"
	"sugar/response"
	"sugar/utils"

	"gopkg.in/guregu/null.v3"
)

// type Budget struct {
// 	Memory string      `json:"memory"`
// 	Cpu    string      `json:"cpu"`
// 	Gpu    null.String `json:"gpu"`
// }

//
func (op *Operation) CreateApp(userID string, name string, trainJobID int64, projectID null.Int, description null.String, budget null.String, replicasMin int, replicasMax int) *response.Response {
	op.BeginOp(UseK8sOption)
	defer op.EndOp()
	if exist, _ := op.DbOp.IsAppExist(userID, name); exist {
		panic(errorcode.AppAlreadyExistError)
	}
	trainJob, _ := op.checkTrainJobAuth(userID, trainJobID)
	if trainJob.Status != 2 {
		panic(errorcode.TrainJobNotSuccessError)
	}
	model, _ := op.checkModelAvailable(userID, trainJob.Model_Id)
	budgetS := Budget{
		Memory: "1000Mi",
		Cpu:    "2",
	}
	if budget.Valid {
		err := json.Unmarshal([]byte(budget.String), &budgetS)
		if err != nil {
			panic(errorcode.JSONOperationError)
		}
	}
	bbudget, _ := json.Marshal(budgetS)

	modelFolderID := utils.GetUUID()
	err := utils.WriteFile("/root/model/"+modelFolderID+"/model.py", model.Model_File_Bytes, 0755)
	if err != nil {
		panic(errorcode.FileOperationError)
	}
	servicePort, err := utils.GetFreePort()
	if err != nil {
		panic(errorcode.NetPortError)
	}
	// datasetFolderPath:=utils.GetEnvDefault("DATASET_FOLDER_PATH", "/root/dataset")
	appID, err := op.DbOp.CreateApp(userID, name, trainJobID, projectID, trainJob.Model_Id, trainJob.Model_Commit_ID, description, string(bbudget), servicePort, replicasMin, replicasMax, trainJob.Type)
	if err != nil {
		panic(errorcode.CreateTrainJobError)
	}
	statusHookURL := fmt.Sprint("http://", utils.GetEnvDefault("KUBERNETES_ADVERTISE_ADDR", "192.168.2.89"), ":", utils.GetEnvDefault("KUBERNETES_ADVERTISE_PORT", "8082"), "/", "app/", appID, "/status")
	err = op.K8sOp.CreateApp(strconv.FormatInt(appID, 10), replicasMin, replicasMax, "model", trainJob.Param_Save_Object.String, model.Model_Class, servicePort, budgetS.Cpu, budgetS.Memory, modelFolderID, statusHookURL)
	if err != nil {
		op.DbOp.UpdateAppStatus(appID, -1)
		// panic(errorcode.KubernetesOperationError)
	} else {
		op.DbOp.UpdateAppStatus(appID, 1)
		go trainJobMonitor(appID)
	}

	res := make(map[string]interface{})
	res["id"] = appID
	return response.NewSuccess(res)
}

func appMonitor(appID int64) {
	op := NewOp()
	op.updateApp(appID)
}

func (op *Operation) updateApp(trainJobID int64) {
	// sTrainJobID := strconv.FormatInt(trainJobID, 10)
	// var status string
	for {
		op.BeginOp(UseK8sOption)
		// status = op.K8sOp.GetAppStatus(sTrainJobID)
		// _ = op.DbOp.UpdateAppStatus(trainJobID, status)
		op.EndOp()
		// if status == "TERMINATED" || status == "FAILED" || status == "SUCCESS" {
		// break
		// }
	}
}

func (op *Operation) checkAppAuth(userID string, appID int64) (*database.App, error) {
	app, err := op.DbOp.GetAppByID(appID)
	if err != nil {
		panic(errorcode.GetAppError)
	}
	if app.User_ID.String() != userID {
		panic(errorcode.NoAppAuthError)
	}
	return app, nil
}

//
func (op *Operation) GetApp(userID string, appID int64) *response.Response {
	op.BeginOp()
	defer op.EndOp()
	app, _ := op.checkAppAuth(userID, appID)
	return response.NewSuccess(app)
}

//
func (op *Operation) StopApp(userID string, appID int64) *response.Response {
	op.BeginOp(UseK8sOption)
	defer op.EndOp()
	op.checkAppAuth(userID, appID)
	err := op.K8sOp.DeleteApp(strconv.FormatInt(appID, 10))
	if err != nil {
		panic(errorcode.DeleteAppError)
	}
	return response.NewSuccess(nil)
}

//
func (op *Operation) ListApp(userID string) *response.Response {
	op.BeginOp()
	defer op.EndOp()
	apps, err := op.DbOp.ListAppByUser(userID)
	if err != nil {
		panic(errorcode.ListAppError)
	}
	return response.NewSuccess(apps)
}

//
func (op *Operation) UpdateAppStatus(appID int64, status string) *response.Response {
	op.BeginOp()
	defer op.EndOp()
	var istatus int
	switch status {
	case "TERMINATED":
		istatus = 4
		break
	case "REBALANCING":
		istatus = 3
		break
	case "RUNNING":
		istatus = 2
		break
	default:
		istatus = 1
		break
	}
	if err := op.DbOp.UpdateAppStatus(appID, istatus); err != nil {
		panic(errorcode.UpdateAppStatusError)
	}
	return response.NewSuccess(nil)
}
